/* Interactive parts of the about page. Each is self-contained and reads its
   copy from ABOUT_EN / ABOUT_CS in about-content.js. */

/* Gradient field. On a desktop pointer the puddles follow the cursor. Anywhere
   without hover - phones, tablets - there is nothing to follow, so the field
   sits still at its resting position instead. Position drives a CSS variable
   either way, so the paint stays on the compositor. */
function GradientField({ children, height = '100%' }) {
  const ref = React.useRef(null);
  /* Same gate as PhotoBlock and the CSS breakpoint: a real pointer AND a
     desktop-width viewport. A touch device that reports (hover: hover) - and
     the phone preview frame does - must still get the static field. */
  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const mq = window.matchMedia('(hover: hover) and (min-width: 1025px)');
    let frame = 0;
    const move = (ev) => {
      if (frame) return;
      frame = requestAnimationFrame(() => {
        frame = 0;
        const r = el.getBoundingClientRect();
        el.style.setProperty('--mx', `${((ev.clientX - r.left) / r.width) * 100}%`);
        el.style.setProperty('--my', `${((ev.clientY - r.top) / r.height) * 100}%`);
      });
    };
    const apply = () => {
      el.removeEventListener('pointermove', move);
      if (mq.matches) { el.addEventListener('pointermove', move); return; }
      /* Back to rest, in case the window shrank mid-hover. */
      el.style.setProperty('--mx', '50%');
      el.style.setProperty('--my', '40%');
    };
    apply();
    mq.addEventListener('change', apply);
    return () => { mq.removeEventListener('change', apply); el.removeEventListener('pointermove', move); if (frame) cancelAnimationFrame(frame); };
  }, []);
  return (
    <div ref={ref} style={{ position: 'relative', height, overflow: 'hidden', background: 'var(--paper)', '--mx': '50%', '--my': '40%' }}>
      <div style={{ position: 'absolute', inset: '-20%', background: 'radial-gradient(46% 46% at var(--mx) var(--my), #e935b0 0%, rgba(233,53,176,0) 62%), radial-gradient(52% 52% at calc(100% - var(--mx)) calc(100% - var(--my)), #3355d1 0%, rgba(51,85,209,0) 66%), radial-gradient(70% 60% at 80% 0%, #f0b06a 0%, rgba(240,176,106,0) 55%)', filter: 'blur(38px)', opacity: 0.5, transition: 'background-position .2s ease-out' }} />
      {/* the field has to dissolve into the page, not stop at an edge */}
      <div style={{ position: 'absolute', left: 0, right: 0, bottom: 0, height: '42%', background: 'linear-gradient(to bottom, rgba(248,248,248,0) 0%, rgba(248,248,248,0.55) 45%, var(--paper) 100%)', pointerEvents: 'none' }} />
      <div style={{ position: 'relative' }}>{children}</div>
    </div>
  );
}

/* Reveals its children one at a time when scrolled into view. */
function Reveal({ children, delay = 0, as = 'div', style = {} }) {
  const ref = React.useRef(null);
  const [on, setOn] = React.useState(false);
  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    if (!('IntersectionObserver' in window)) { setOn(true); return; }
    const io = new IntersectionObserver(([e]) => { if (e.isIntersecting) { setOn(true); io.disconnect(); } }, { threshold: 0.15, rootMargin: '0px 0px -8% 0px' });
    io.observe(el);
    return () => io.disconnect();
  }, []);
  const Tag = as;
  return (
    <Tag ref={ref} style={{
      ...style,
      opacity: on ? 1 : 0,
      transform: on ? 'translateY(0)' : 'translateY(18px)',
      transition: `opacity .7s cubic-bezier(.22,1,.36,1) ${delay}ms, transform .7s cubic-bezier(.22,1,.36,1) ${delay}ms`,
    }}>{children}</Tag>
  );
}

/* Career timeline: a sticky year rail on the left, entries that light up as
   they pass the reading line. */
function CareerTimeline({ data }) {
  const [active, setActive] = React.useState(0);
  const refs = React.useRef([]);
  React.useEffect(() => {
    if (!('IntersectionObserver' in window)) return;
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => {
        if (e.isIntersecting) setActive(Number(e.target.dataset.i));
      });
    }, { threshold: 0.55, rootMargin: '-20% 0px -35% 0px' });
    refs.current.forEach((el) => el && io.observe(el));
    return () => io.disconnect();
  }, [data.entries.length]);

  const mono = { fontFamily: 'var(--font-mono)', fontWeight: 500, fontSize: 9.5, letterSpacing: 'var(--tracking-label)', textTransform: 'uppercase' };
  return (
    <div className="sm-about-timeline" style={{ display: 'grid', gridTemplateColumns: '210px 1fr', gap: 48, alignItems: 'start' }}>
      <div className="sm-timeline-rail" style={{ position: 'sticky', top: 96 }}>
        <div style={{ ...mono, color: 'var(--gray-500)', marginBottom: 20 }}>{data.entries[active].tag}</div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
          {data.entries.map((e, i) => (
            <div key={e.year} style={{
              fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 40, lineHeight: 1.1, letterSpacing: '-0.02em',
              color: i === active ? 'var(--black)' : 'var(--gray-300)',
              transform: i === active ? 'translateX(6px)' : 'none',
              transition: 'color .35s ease, transform .35s cubic-bezier(.22,1,.36,1)',
            }}>{e.year}</div>
          ))}
        </div>
      </div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
        {data.entries.map((e, i) => (
          <div key={e.year} data-i={i} ref={(el) => { refs.current[i] = el; }}
            style={{
              background: 'var(--white)', borderRadius: 'var(--radius-card)', padding: '34px 34px 32px',
            }}>
            <div style={{ ...mono, color: 'var(--gray-500)' }}>{e.year} · {e.org}</div>
            <h3 style={{ margin: '14px 0 0', fontFamily: 'var(--font-display)', fontWeight: 600, fontSize: 30, lineHeight: 1.14, letterSpacing: '-0.015em', color: 'var(--black)' }}>{e.role}</h3>
            <p style={{ margin: '16px 0 0', fontFamily: 'var(--font-body)', fontSize: 17, lineHeight: 1.7, color: i === active ? 'var(--gray-700)' : 'var(--gray-600)', maxWidth: '58ch' }}>{String(e.text).split('\n').map((line, k) => (<React.Fragment key={k}>{k > 0 && <span style={{ display: 'block', height: '0.85em' }} />}{line}</React.Fragment>))}</p>
          </div>
        ))}
      </div>
    </div>
  );
}

/* The craft. Same lesson as the services rebuild: a click-driven rail with one
   panel beats hover-dimming - no flicker as the cursor crosses, and the depth
   distinction is stated in words rather than encoded in font weight, which a
   reader has to be told to decode. */
function SkillMap({ data, tone = 'light' }) {
  const dark = tone === 'dark';
  const [active, setActive] = React.useState(0);
  const mono = { fontFamily: 'var(--font-mono)', fontWeight: 500, fontSize: 9.5, letterSpacing: 'var(--tracking-label)', textTransform: 'uppercase' };
  const group = data.groups[active];

  return (
    <div className="sm-about-skills" style={{ display: 'grid', gridTemplateColumns: '300px minmax(0, 1fr)', gap: dark ? 'clamp(24px, 4vw, 56px)' : 10, alignItems: 'start' }}>
      <div style={{ display: 'flex', flexDirection: 'column', gap: dark ? 0 : 2 }}>
        {data.groups.map((g, i) => {
          const on = i === active;
          return (
            <button key={g.name} onClick={() => setActive(i)} aria-current={on}
              style={{
                textAlign: 'left', border: 'none', cursor: 'pointer', borderRadius: dark ? 0 : 'var(--radius-card)',
                padding: dark ? '16px 4px' : '16px 20px', display: 'grid', gridTemplateColumns: '34px minmax(0, 1fr) auto', gap: 10, alignItems: 'baseline',
                backgroundColor: dark ? 'transparent' : (on ? 'var(--black)' : 'var(--white)'),
                borderTop: dark ? '1.5px solid var(--border-hairline-dark)' : 'none',
                transition: 'background-color .3s ease',
              }}>
              <span style={{ ...mono, color: dark ? (on ? 'var(--orange-500)' : 'var(--gray-on-dark-500)') : (on ? 'var(--gray-on-dark-400)' : 'var(--gray-500)'), transition: 'color .3s ease' }}>{String(i + 1).padStart(2, '0')}</span>
              <span style={{ fontFamily: 'var(--font-display)', fontWeight: 500, fontSize: 17, lineHeight: 1.25, letterSpacing: '-0.015em', color: dark ? (on ? 'var(--white)' : 'var(--gray-on-dark-400)') : (on ? 'var(--white)' : 'var(--black)'), transition: 'color .3s ease' }}>{g.name}</span>
              <span style={{ ...mono, color: dark ? (on ? 'var(--orange-500)' : 'var(--gray-on-dark-500)') : (on ? 'var(--gray-on-dark-400)' : 'var(--gray-500)'), transition: 'color .3s ease' }}>{g.items.length}</span>
            </button>
          );
        })}
      </div>
      <div key={active} style={{ background: dark ? 'transparent' : 'var(--white)', borderRadius: dark ? 0 : 'var(--radius-card)', padding: dark ? 0 : 'clamp(20px, 3vw, 30px) clamp(24px, 3vw, 34px) clamp(26px, 3vw, 34px)', animation: 'sm-symptom-in .45s cubic-bezier(.22,1,.36,1) both' }}>
        <div className="sm-craft-list" style={{ display: 'grid', gridTemplateColumns: 'repeat(2, minmax(0, 1fr))', columnGap: 36 }}>
          {group.items.map((i) => (
            <div key={i} style={{ display: 'grid', gridTemplateColumns: '20px minmax(0, 1fr)', gap: 10, alignItems: 'baseline', padding: '13px 0', borderTop: dark ? '1.5px solid var(--border-hairline-dark)' : '1.5px solid var(--gray-200)' }}>
              <span style={{ width: 7, height: 7, borderRadius: 2, background: dark ? 'var(--gray-on-dark-500)' : 'var(--black)', display: 'block', transform: 'translateY(-2px)' }} />
              <span style={{ fontFamily: 'var(--font-body)', fontSize: 16, lineHeight: 1.45, color: dark ? 'var(--gray-on-dark-200)' : 'var(--gray-700)' }}>{i}</span>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { GradientField, Reveal, CareerTimeline, SkillMap });
